/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package idea.sfo.viewer.designer;


import designersfo.Cons;
import designersfo.ConsV;
import java.util.TreeMap;
import org.openide.util.Exceptions;


/**
 * This class wraps a <gids-designer:cons-v> object for the purpose of using and
 * displaying it within an explorer window and view.
 *
 * @author renke002
 */
public class DesignerRow
{

    /**
     * Column Number to Cell object (Cons) mapping.
     */
    private TreeMap< Integer , Cons> colMap;
    /**
     * Column Number to Cell width mapping.
     *
     * NOTE: the number of mapping must match those in colMap!
     */
    private TreeMap< Integer , Long> colWidths;
    /**
     * Cumulative heights in FUs of the rows before this one.
     */
    private long prevRowHeights;
    /**
     * Height in FUs of this row.
     */
    private long rowHeight;
    /**
     * Width in FUs of this row.
     */
    private long rowWidth;
    /**
     * Number of this row.
     */
    private final Integer rowNumber;
    /**
     *
     */
    private ConsV rowConsV;


    /**
     *
     * @param row
     * @param cv
     * @param widths
     * @param prevRowHeights
     */
    public DesignerRow( int row , ConsV cv , TreeMap< Integer , Long> widths , long prevRowHeights )
    {
        this.colWidths = widths;
        this.prevRowHeights = prevRowHeights;
        this.rowConsV = cv;
        this.rowNumber = row;

        if ( cv.getCons().isEmpty() == false )
        {
            this.colMap = this.parseConsVChildren(
                    cv.getCons().get( DesignerRowChildren.FIRST_ELEMENT ) ,
                    new TreeMap< Integer , Cons>() );
        }
        else
        {
            // what to do?
            this.colMap = new TreeMap<>();
            this.parseConsVChildren( cv.getCons().get( DesignerRowChildren.FIRST_ELEMENT ) );

        }
        if ( this.colMap.size() != this.colWidths.size() )
        {
            Exceptions.printStackTrace(
                    new Exception( "The number of columns " + this.colMap.size()
                    + " does not match the number of column widths: " + this.colWidths.size() ) );
        }
        this.rowWidth = this.calculateRowWidth();
    }


    /**
     *
     * @return
     */
    private long calculateRowWidth()
    {
        long width = 0;
        if ( this.colWidths != null && this.colWidths.isEmpty() == false )
        {
            for ( Integer col : this.colWidths.keySet() )
            {
                width += this.colWidths.get( col );
            }
        }
        return width;
    }


    /**
     *
     * @return - mapping between column number and cell object.
     */
    public TreeMap<Integer , Cons> getColMap()
    {
        return this.colMap;
    }


    /**
     *
     * @return - mapping between column number and width.
     */
    public TreeMap<Integer , Long> getColWidths()
    {
        return this.colWidths;
    }


    /**
     *
     * @return
     */
    public String getName()
    {
        return "Row: " + this.rowNumber;
    }


    /**
     *
     * @return the number of columns contained in this row
     */
    public int getNumberOfCols()
    {
        return this.colMap.size();
    }


    /**
     *
     * @return - the cumulative heights in FUs of the rows before this one.
     */
    public long getPrevRowHeights()
    {
        return this.prevRowHeights;
    }


    /**
     *
     * @return
     */
    public long getRowHeight()
    {
        return this.rowHeight;
    }


    /**
     *
     * @return
     */
    public Integer getRowNumber()
    {
        return this.rowNumber;
    }


    /**
     *
     * @return
     */
    public ConsV getRowConsV()
    {
        return this.rowConsV;
    }


    /**
     *
     * @return
     */
    public long getRowWidth()
    {
        return this.rowWidth;
    }


    /**
     *
     * As per the <a
     * href="https://censuswiki.fenestra.com/bin/view/Main/DesignerMementoSpec">
     * Designer memento language specification </a> a Cons element will either
     * contain a 'cell-info' object and a reference to the adjacent Cons OR a
     * 'row-info' object and a reference to an 'empty-list' object.
     *
     * This method will parse all the Cons objects attached to this ConsV and
     * add them to the global column_number -> cell_object mapping.
     *
     * @param cons
     * @return
     */
    private void parseConsVChildren( Cons cons )
    {
        if ( cons.getRowInfo().isEmpty() == false )
        {
            /* If there is a row-info object then this is the last cons of the row
             * and the row-info object will have a 'cell-size-v' that defines the
             * vertical size (height) of this row in 'fenestra units'.
             */
            String fu_size = cons.getRowInfo().get( DesignerRowChildren.FIRST_ELEMENT ).getCellSizeV();
            this.rowHeight = Long.parseLong( fu_size.replace( "fu" , "" ) );
        }
        else
        {
            /* Since there isn't a 'row-info' object, there should be 'cell-info'
             * and the next cons. Add the current cons at the last position of the
             * mapping and make a recursive call so we can repeat the process with
             * the adjacent cons.
             */
            this.colMap.put( this.colMap.size() , cons );
            // this check shouldn't be necessary, however it's here to avoid runtime
            // exceptions from an ill-constructed SFO file.
            if ( cons.getCons().isEmpty() == false )
            {
                this.parseConsVChildren( cons.getCons().get( DesignerRowChildren.FIRST_ELEMENT ) );
            }
        }
    }


    /**
     *
     * As per the <a
     * href="https://censuswiki.fenestra.com/bin/view/Main/DesignerMementoSpec">
     * Designer memento language specification </a> a Cons element will either
     * contain a 'cell-info' object and a reference to the adjacent Cons OR a
     * 'row-info' object and a reference to an 'empty-list' object.
     *
     * @param cons
     * @param mapping
     * @return
     */
    private TreeMap< Integer , Cons> parseConsVChildren( Cons cons , TreeMap< Integer , Cons> mapping )
    {
        if ( cons.getRowInfo().isEmpty() == false )
        {
            /* If there is a row-info object then this is the last cons of the row
             * and the row-info object will have a 'cell-size-v' that defines the
             * vertical size (height) of this row in 'fenestra units'.
             */
            String fu_size = cons.getRowInfo().get( DesignerRowChildren.FIRST_ELEMENT ).getCellSizeV();
            this.rowHeight = Long.parseLong( fu_size.replace( "fu" , "" ) );
        }
        else
        {
            /* Since there isn't a 'row-info' object, there should be 'cell-info'
             * and the next cons. Add the current cons at the last position of the
             * mapping and make a recursive call so we can repeat the process with
             * the adjacent cons.
             */
            mapping.put( mapping.size() , cons );
            // this check shouldn't be necessary, however it's here to avoid runtime
            // exceptions from an ill-constructed SFO file.
            if ( cons.getCons().isEmpty() == false )
            {
                mapping = this.parseConsVChildren( cons.getCons().get( DesignerRowChildren.FIRST_ELEMENT ) , mapping );
            }
        }
        return mapping;
    }

}
